﻿using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace MicroMVVM.Navigation
{
    public class Navigator
    {
        #region IsEnabled

        /// <summary>
        /// IsEnabled Attached Dependency Property
        /// </summary>
        public static readonly DependencyProperty IsEnabledProperty =
            DependencyProperty.RegisterAttached("IsEnabled", typeof (bool), typeof (Navigator),
                                                new PropertyMetadata(false, OnIsEnabledChanged));

        /// <summary>
        /// Gets the IsEnabled property. This dependency property 
        /// indicates whether the navigator is enabled for this view.
        /// </summary>
        public static bool GetIsEnabled(DependencyObject d)
        {
            return (bool) d.GetValue(IsEnabledProperty);
        }

        /// <summary>
        /// Sets the IsEnabled property. This dependency property 
        /// indicates whether the navigator is enabled for this view.
        /// </summary>
        public static void SetIsEnabled(DependencyObject d, bool value)
        {
            d.SetValue(IsEnabledProperty, value);
        }

        /// <summary>
        /// Handles changes to the IsEnabled property.
        /// </summary>
        private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var isEnabled = (bool) d.GetValue(IsEnabledProperty);

            if (isEnabled == false) return;

            if (d as Frame != null)
            {
                var frame = d as Frame;

                frame.Navigated += FrameNavigatedAndDispose;
            }

            if (d as UserControl != null)
            {
                var userControl = d as UserControl;

                NavigatingCancelEventHandler frameOnNavigating = null;

                userControl.Loaded += (sender, routedEventArgs) =>
                                          {
                                              var control = sender as UserControl;

                                              if (control == null) return;

                                              var page = (sender is Page)
                                                             ? sender as Page
                                                             : NavigationHelpers.GetParent<Page>(control) as Page;

                                              if (page == null) return;

                                              var frame = page.Frame;

                                              if (frame == null) return;

                                              frame.Navigated += FrameNavigated;

                                              var dataContext = control.DataContext;

                                              if (dataContext == null) return;

                                              var viewModel = dataContext as INavigation;

                                              if (viewModel == null) return;

                                              viewModel.Current = frame;

                                              frameOnNavigating = delegate { viewModel.OnNavigating(); };

                                              frame.Navigating += frameOnNavigating;
                                          };

                userControl.Unloaded += (sender, routedEventArgs) =>
                                            {
                                                var control = sender as UserControl;

                                                if (control == null) return;

                                                var page = (sender is Page)
                                                               ? sender as Page
                                                               : NavigationHelpers.GetParent<Page>(control) as Page;

                                                if (page == null) return;

                                                var frame = page.Frame;

                                                if (frame == null) return;

                                                frame.Navigated -= FrameNavigated;

                                                var dataContext = control.DataContext;

                                                if (dataContext == null) return;

                                                var viewModel = dataContext as INavigation;

                                                if (viewModel == null) return;

                                                viewModel.Current = null;

                                                frame.Navigating -= frameOnNavigating;
                                            };
            }
        }

        private static void FrameNavigated(object sender, NavigationEventArgs navigationEventArgs)
        {
            Navigated(navigationEventArgs);
        }

        private static void FrameNavigatedAndDispose(object sender, NavigationEventArgs navigationEventArgs)
        {
            var frame = Navigated(navigationEventArgs);

            if (frame == null) return;

            frame.Navigated -= FrameNavigatedAndDispose;
        }

        private static Frame Navigated(NavigationEventArgs navigationEventArgs)
        {
            var page = navigationEventArgs.Content as Page;

            if ((page == null) || (page.DataContext == null)) return null;

            var destination = page.DataContext as INavigation;

            if (destination == null) return null;

            destination.OnNavigated(navigationEventArgs.Parameter);

            return page.Frame;
        }

        #endregion
    }
}